Shiny é um pacote do R (também disponível para o Python) que permite a criação e publicação de aplicações web sem necessidade de conhecimento prévio em HTML, CSS e JavaScript;
Ou seja, a proposta é facilitar a criação de apps web somente com conhecimento em R.
Em essência, um App Shiny é gerado a partir de dois objetos e a chamada de uma função:
UI (Interface do Usuário)
Server
Função shinyApp(ui, server)
O objeto UI é geralmente inicializado pela função fluidPage(), que gerá uma página HTML vazia (também podem ser usadas outras funções para inicializar o UI);
São definidos os tipos de inputs (valores de entrada) e outputs (valores de saída) que o App Shiny apresentará, assim como também a formatação, layout.
Há inúmeros tipos de input disponíveis no Shiny.De todo modo, para que sejam reconhecidos e processados, todos ele precisam de dois parâmetros em comum:
Parâmetro “inputId”: Trata-se de uma string simples e única que será armazenada dentro da lista “input” que é parâmetro da função server(input, output). Desse modo, deve-se utilizar id´s diferentes para referenciar diferentes inputs;
Parâmetro “label”: Trata-se do rótulo/mensagem/instrução que irá aparecer no App Shiny, orientando o usuário sobre como ele deve interagir com as opções de input dadas.
Dada uma listagem de opções fornecidas, é solicitado ao usuário selecionar uma ou algumas das opções dadas.
Semelhante à função selectInput(), mas com uma aparência levemente diferente, e permite selecionar, por padrão, múltiplas opções.
Pede ao usuário que aperte um botão interativo para que uma certa ação seja realizada no aplicativo.
A partir de uma barra de rolagem (horizontal), o usuário é informado para selecionar um número dentro de um intervalo de valores estabelecido
Trata-se de uma função para inserção de inputs em forma de texto.
Semelhante a função tenxtInput(), com a diferença que o input informado aparecerá com tarjas (“*”)
Além das funções input citadas acima, há várias outras tais como:
Os tipos de output são definidos no primeiro objeto, a UI (interface de usuário).
Assim como os inputs, no output também exigem uma identificação:
dataTableOutput(outputId = "dataframe") # Define um dataframe como output
htmlOutput(outputId = "html") # Define um documento HTML como output
imageOutput(outputId = 'image') # Define uma imagem como output
plotOutput(outputId = 'plot') # Define um gráfico como output
tableOutput(outputId = 'tabela') # Define uma tabela como output
textOutput(outputId = 'texto') # Define um texto como output
...Feito isso, para que o(s) output(s) definido(s) seja(m) devidamente executado(s) e processado(s) pelo R, faz-se necessário também o uso das render functions (funções de renderização); por exemplo, “renderText({})”;
Estas são definidas no objeto Server (falaremos sobre logo a seguir).
As render functions (funções de renderização) são os objetos que irão efetivamente explicitar os outputs previamente definidos no UI;
Alguns exemplos:
| Output | Render Function |
|---|---|
| textOutput | renderText({}) |
| plotOutput | renderPlot({}) |
| tableOutput | renderTable({}) |
| imageOutput | renderImage({}) |
Elas possibilitam também a utilização de outros pacotes interativos possuem suporte para renderizar seus outputs no Shiny. Entre eles DT, Plotly e Leaflet
Na estrutura básica os inputs são dispostos um embaixo do outro, visando melhorar a visualização do usuário, existem funções que alteram esse padrão:
titlePanel(): que implementa um título pro app:
sidebarLayout(): essa cria o layout geral de saída e que comporta opcções de disposição de inputs e outputs.
Aqui a função tem como parametros apenas o label que está em formato de string
No layout gerado por essa função divide-se a tela de saída em duas:
sidebarPanel(): aqui estarão as entradas do usário;
mainPanel(): nesta área são as saídas geradas no server.
Neste espaço as opções de inputs vistas anteriormente são posicionadas no lado esquerdo da interface;
São possíveis envolopar mais de uma opção de input.
inputPanel(
titlePanel('BMI Calculator'),
sidebarLayout(
sidebarPanel(
textInput('name', 'Enter your name'),
numericInput('height', 'Enter height (in m)', 1.5, 1, 2, step = 0.1),
numericInput('weight', 'Enter weight (in Kg)', 60, 45, 120),
actionButton("show_bmi", "Show BMI")
),
mainPanel()
)
)Nessa função vamos incluir as saídas que foram processadas no server e adicionadas no lado direito da interface;
Aqui caso haja uma saída basta apenas utilizar:
ui <- fluidPage(
titlePanel('BMI Calculator'),
sidebarLayout(
sidebarPanel(
textInput('name', 'Enter your name'),
numericInput('height', 'Enter height (in m)', 1.5, 1, 2, step = 0.1),
numericInput('weight', 'Enter weight (in Kg)', 60, 45, 120),
actionButton("show_bmi", "Show BMI")
),
mainPanel(
textOutput("bmi")
)
)
)Caso existam mais de uma saída e deseja-se criar abas para exibir os resultados vamos usar essa função.
ui <- fluidPage(
titlePanel("UFO Sightings"),
sidebarPanel(
selectInput("state", "Choose a U.S. state:", choices = unique(usa_ufo_sightings$state)),
dateRangeInput("dates", "Choose a date range:",
start = "1920-01-01",
end = "1950-01-01"
)
),
mainPanel(
tabsetPanel(
tabPanel("Plot", plotOutput("shapes")),
tabPanel("Table", tableOutput("duration_table"))
)
)
)Resumidamente, reatividade em programação é o conceito que torna possível a interação dinâmcica do usuário com o programa, no qual o que é efetivamente executado e exibido se baseia no acompanhamento das mudanças de determinados valores. Por exemplo, se um determinado input muda, um novo cálculo deve ser executado e um novo output gerado.
A reatividade se baseia em uma paradigma diferente de programção, chamado de declarativo. No Shiny, declaramos dentro da função server() quando e quais códigos devem ser executados a depender de cada nova situação. As relações de dependência entre input e output e os consequentes fluxos de execução nesses casos são definidadas e representadas através de um diagrama de reatividade.
exemplo:
fonte: https://programando-em-shiny.curso-r.com
exemplo:
# server
server <- function(input, output, session) {
output$histograma_A <- renderPlot({
print("Gerando histograma A...")
hist(mtcars[[input$variavel_A]], main = "Histograma A")
})
output$histograma_B <- renderPlot({
print("Gerando histograma B...")
hist(mtcars[[input$variavel_B]], main = "Histograma B")
})
}
# app
shinyApp(ui, server)fonte: https://programando-em-shiny.curso-r.com
diagrama de reatividade do exemplo
fonte: https://programando-em-shiny.curso-r.com
Para um app Shiny funcionar corretamente, é necessário que o diagrama de reatividade seja definido adequadamente na função server(). E para isso, alguns princípios devem ser observados. O primeiro deles é o seguinte: o diagrama de reatividade deve começar em um valor reativo e terminar em uma função observadora
fonte: https://programando-em-shiny.curso-r.com
valores reativos são objetos que ativam a reatividade a partir das mudanças em seus valores. O principal exemplo de valor reativo são os próprios objetos do input.
funções observadoras são funções que acompanham as mudanças no valores reativos e executam algo diferente a partir delas. O principal exemplo de função reativas são as funções da famílias render*().
fonte: https://programando-em-shiny.curso-r.com
Expressões Reativas são objetos especiais muito importantes que atuam como valor reativo e função observadora ao mesmo tempo. São utilizadas em situações onde um valor reativo utilizado dentro de uma função observadora depende outro valor reativo.
fonte: https://programando-em-shiny.curso-r.com
Sua utilidade se torna mais claro quando levamos em consideração um segundo princípio importante da reatividade no shiny: valores e expressões reativas só podem ser lidas dentro de um contexto reativo.
Ou seja, em um caso onde precisemos criar uma variável que dependa de um input, mas que sirva para ativar a reatividade em outputs diferentes, precisamos de uma expressão reativa, por exemplo.
fonte: https://programando-em-shiny.curso-r.com
exemplo:
Imagine uma aplicação Shiny que simule o lançar de um dado simples de 6 faces. Queremos que o usário insira o número de lançamentos (tamanho da amostra) desejado e que a partir desse número seja exibido um gráfico de barras com o quantidade de aparições de cada número (distribuição observada) e um frase informando qual valor mais apareceu.
fonte: https://programando-em-shiny.curso-r.com
exemplo - primeira abordagem:
fonte: https://programando-em-shiny.curso-r.com
exemplo - primeira abordagem:
server <- function(input, output, session) {
lancamentos <- sample(1:6, input$tamanho, replace = TRUE)
output$distribuicao <- renderPlot({
lancamentos |>
table() |>
barplot()
})
output$frase <- renderText({
contagem <- table(lancamentos)
mais_freq <- names(contagem[which.max(contagem)])
num_ap <- contagem[mais_freq]
paste("o valor mais sorteado foi o", mais_freq, "com ", num_ap, "aparições")
})
}
shinyApp(ui, server)Error in input$tamanho :
Can't access reactive value 'tamanho' outside of reactive consumer.
ℹ Do you need to wrap inside reactive() or observe()?
fonte: https://programando-em-shiny.curso-r.com
Por que deu erro?
Justamente porque não seguimos o segundo princípio, e tentamos acessar o valor reativo input$tamanho dentro da variável comum lancamentos.
Qual é abordagem correta?
Podemos resolver criando o objeto lancamentos como uma expressão reativa através da função observadora reactive().
fonte: https://programando-em-shiny.curso-r.com
exemplo - abordagem adequada
Dentro de server(), ao invés de:
Fazemos:
reactive()A função reactive() cria uma expressão reativa que observa todos os valores reativos presentes dentro de seu código.
No exemplo anterior, criamos a expressão reativa lancamentos cujo valor é recalculado sempre que o valor reativo input$tamanho mudar, e utilizamos seu valor chamando lancamentos() dentro das funções observadoras renderPlot() e renderText().
fonte: https://programando-em-shiny.curso-r.com
eventReactive()A função eventReactive() é similar, mas observa mudanças em apenas um valor reativo, especificado na chamada da própria função.
Geralmente é utilzada quando queremos atrasar (delay) a ativação da função observadora.
Um caso comum de uso é quando temos um botão na UI, criado com a função actionButton(), por exemplo.
eventReactive()Podemos utilizar essa ideia para incrementar o nosso exemplo anterior, de tal forma que os outputs só serão gerados caso o botão seja clicado, independente se os valores de input$tamanho mudarem.
Adicionamos a função actionButton() da seguinte maneira:
fonte: https://programando-em-shiny.curso-r.com
eventReactive()E atribuímos ao objeto lancamentos dentro do server() a função eventReactive() tendo input$botao como primeiro argumento:
fonte: https://programando-em-shiny.curso-r.com
observe() E observeEvent()A funções observe() e observetEvent() tem o conceito semelhante a reactive() e eventReactive(), porém possuem uma diferença fundamental: elas não geram expressões reativas, sendo utilizadas apenas para rodar determinado código caso uma mudança em algum valor reativo aconteça. Ou seja, não podemos definir a expressão reativa lancamentos do exemplo anterior com elas.
fonte: https://programando-em-shiny.curso-r.com
observe() E observeEvent()Geralmente são utilizadas quando queremos usar a reatividade para disparar (trigger) ações que não estão ligadas à geração de outputs, como o registro de informações em bases de dados ou o envio de e-mails, por exemplo.
fonte: https://programando-em-shiny.curso-r.com
isolate()Utilizada para isolar um valor reativo específico entre vários, de tal forma que se apenas ele for alterado nenhuma reatividade é disparada.
fonte: https://shiny.posit.co/r/getstarted/build-an-app/reactivity-essentials/stop-trigger-delay.html
reactiveVal() E reactiveValues()Utilizada para gerar apenas um (reactiveVal) ou mais (reactiveValues) valores reativos mutáveis, diferentes do objetos do input, que são imutáveis.
Pode ser usada em alguns casos quando queremos controlar valores reativos dentro do server(), e renderizar mudanças na UI a partir deles.
fonte: https://programando-em-shiny.curso-r.com
req() E validate()Utilizada para validar valores reativos. Nesse contexto inválido indica um objeto:
• FALSE
• NULL
• "", uma string vazia
• Um vetor vazio
• Um vetor que contenha apenas NA
• Um vetor lógico que contenha apenas FALSE ou NA
• Um objeto com classe try-error
• Um valor reativo que represente um actionButton() que ainda não foi clicado
fonte: https://programando-em-shiny.curso-r.com
req() E validate()A função req() retorna um erro silencioso. Com o qual podemos definir que o server() e a ui() devem manter as coisas como estavam antes.
Já a função validate() permite customizar o erro retornado.
fonte: https://programando-em-shiny.curso-r.com
# Definindo a sidebar e seus parâmetros
sidebar <- dashboardSidebar(width = 300,
sidebarMenu(
id = "pages",
menuItem("Many charts", tabName = "charts",
icon = icon("chart-line")),
menuItem("Statistics", tabName = "stats",
icon = icon("file-excel"))
))
ui <- dashboardPage(header, sidebar, body)
server <- function(input, output) {
}
shinyApp(ui, server) # Adicionando subtabs na sidebar
sidebar <- dashboardSidebar(
width = 300,
sidebarMenu(
id = "pages",
menuItem("Many charts", tabName = "charts",
icon = icon("chart-line")),
menuItem("Statistics", tabName = "stats",
icon = icon("file-excel"),
menuSubItem("Team 1", tabName = "team1",
icon = icon("user")))
))
ui <- dashboardPage(header, sidebar, body)
server <- function(input, output) {
}
shinyApp(ui, server)# Adicionando inputs na sidebar
sidebar <- dashboardSidebar(width = 300,
sidebarMenu(
id = "pages",
menuItem("Many charts", tabName = "charts",
icon = icon("chart-line")),
menuItem("A couple of checkboxes",
checkboxGroupInput("checkboxes",
"Days of the week",
choices = c("Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun")))
))
ui <- dashboardPage(header, sidebar, body)
server <- function(input, output) {
}
shinyApp(ui, server)